require 'colored'
require 'mini_magick'
require 'json'

DEVICES_TO_SKIP = [
  "Apple iMac Retina.png", # we don't currently support iMac
  "Apple iMac.png" # we don't currently support iMac
]

DEVICE_COLORS = [
  "Space Gray",
  "Rose Gold",
  "Jet Black",
  "Matte Black",
  "Silver",
  "Gold",
  "Green",
  "Blue",
  "Red",
  "White",
  "Yellow",
  "Gray",
  "Coral"
]

task(:generate_device_frames) do
  MiniMagick.configure do |config|
    config.validate_on_create = false
  end

  puts("Download and extract the latest devices from Facebook")
  puts("")
  puts("  https://facebook.design/devices")
  puts("")
  confirm
  puts("Move the content of the zip file into './raw'")
  mkdir("raw") unless File.directory?("raw")
  system("open ./raw")
  confirm

  # Let's find all the image resources we need and copy it into the correct directory
  current_time = Time.now.to_i.to_s
  output = File.join("output", current_time)
  sh("rm -rf output")
  mkdir_p(output) unless File.directory?(output)

  image_assets = []
  devices = Hash.new { |h, k| h[k] = [] }
  (Dir["raw/Phones/Apple*/Device/*.png"] + Dir["raw/Computers/Apple*/Device/*.png"] + Dir["raw/Tablets/Apple*/Device/*.png"]).each do |current_raw|
    basename = File.basename(current_raw)
    next if DEVICES_TO_SKIP.include?(basename)

    cp(current_raw, output)
    output_file_path = File.join(output, basename)
    sanitized_basename = sanitize_filename(basename)
    sanitized_file_path = File.join(output, sanitized_basename)
    mv(output_file_path, sanitized_file_path) unless output_file_path == sanitized_file_path
    convert_image_resource(sanitized_file_path)
    image_assets << sanitized_basename

    x, y, width = measure_slot(sanitized_file_path)
    device_name = sanitize_device_name(sanitized_basename)
    devices[device_name] << { filename: sanitized_basename, x: x, y: y, width: width }
  end

  raise "Could not find any images, make sure to copy all extracted files into ./raw" if image_assets.count < 10

  File.write(File.join(output, "files.json"), image_assets.to_json)
  puts("Wrote names of available files to files.json")

  File.write(File.join(output, "version.txt"), current_time)
  puts("Wrote the current time stamp to version.txt")

  # Create `offests.json` file
  offsets = {}
  devices.each do |device, models|
    offsets[device] = { offset: "+#{models[0][:x]}+#{models[0][:y]}", width: models[0][:width] }
  end
  File.write(File.join(output, "offsets.json"), JSON.pretty_generate({ 'portrait' => offsets }))
  puts("Wrote the slot offsets to offsets.json")

  # Check if the offsets for a device are identical for all colors
  devices.each do |device, models|
    next if models.all? { |m| m[:x] == (models[0][:x]) && m[:y] == (models[0][:y]) && m[:width] == (models[0][:width]) }
    puts
    puts("The offsets for #{device} are not identical for all colors:".red)
    models.each { |m| puts("#{m[:filename]} +#{m[:x]}+#{m[:y]}, #{m[:width]}") }
    puts("You can either keep the value in `offset.json` or replace it with a more appropriate value from above.")
    puts
  end

  cp_r(output, "output/latest")

  system("open ./output")
  puts("Done writing all the things to './output', please upload both the 'latest' and the time stamp '#{current_time}' folder to GitHub".green)
  puts("To do so, clone https://github.com/fastlane/frameit-frames, make sure to check out the `gh-pages` branch".green)
  puts("and overwrite the 'latest' folder, and copy the time stamp folder into the root.".green)
  puts("Push the changes, and run `fastlane frameit update_frames` to ensure everything worked as expected".green)
end

def confirm
  puts("Press any key when you're ready to continue".yellow)
  STDIN.gets
end

def sanitize_filename(filename)
  filename.gsub('Grey', 'Gray')
end

# This will remove the white space around the actual frame
def convert_image_resource(path)
  puts("Updating and trimming image file '#{path}'")
  image = MiniMagick::Image.open(path)
  image.combine_options do |co|
    co.fuzz("90%") # color disimilarity threshold to get all shadows, see https://github.com/fastlane/fastlane/pull/14199
    co.trim("+repage") # `+repage` removes meta-data
  end
  image.write(path)
end

def measure_slot(path)
  tmp_output = Time.now.to_i.to_s + ".png"

  begin
    command_output = MiniMagick::Tool::Magick.new do |magick|
      magick << path
      magick.negate # it makes white, black and black, white, adjusting all the colors to match
      # connected-component analysis uniquely labels connected components in an image
      magick.define("connected-components:verbose=true") # it enables a verbose output to the shell console
      magick.define("connected-components:area-threshold=100") # it eliminates small objects by merging them with their larger neighbors
      magick.connected_components("8") # it visits 8 neighbors
      magick.auto_level
      magick << tmp_output
    end
  rescue StandardError => e
    puts(e)
    puts("The functionality of slot measurements does not work with ImageMagick older than 7.x".red)
    exit!
  end

  sh("rm #{tmp_output}", verbose: false)

  # The example of the command ouput:
  #
  # Objects (id: bounding-box centroid area mean-color):
  #   494: 640x1136+67+239 386.5,806.5 727040 srgb(255,255,255)
  #   4: 767x1605+0+0 385.4,801.2 478199 srgb(0,0,0)
  #   522: 115x1004+0+601 12.1,1244.6 10023 srgb(255,255,255)
  #   0: 517x232+0+0 125.3,36.3 8303 srgb(255,255,255)
  #   8: 131x115+636+0 731.1,26.2 3908 srgb(255,255,255)
  #   561: 108x108+659+1497 740.4,1578.0 2954 srgb(255,255,255)
  #   500: 7x48+0+301 3.0,323.7 325 srgb(255,255,255)
  #   511: 7x42+0+454 3.0,473.7 283 srgb(255,255,255)
  #
  raw_dimensions = command_output.split("\n")[1].strip
  dimensions = /(\d+)x(\d+)\+(\d+)\+(\d+)/.match(raw_dimensions)
  x = dimensions[3]
  y = dimensions[4]
  width = dimensions[1]
  height = dimensions[2]
  return x.to_i, y.to_i, width.to_i, height.to_i
end

def sanitize_device_name(filename)
  basename = File.basename(filename, File.extname(filename))
  basename = basename.gsub("Apple", "")
  basename = basename.gsub("-", " ")
  DEVICE_COLORS.each { |color| basename.slice!(color) }
  basename.strip.to_s
end

task(default: [:generate_device_frames])
